package com.lb.uts.client.annotation;

import com.alibaba.fastjson.JSONObject;
import com.lb.uts.client.model.ClientDetail;
import com.lb.uts.client.model.TaskJobDetails;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.ibatis.session.SqlSessionFactory;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * 自动注册定时任务
 * 不使用其他方法是因为：如果系统启动失败，那么还需要将已经注册的定时任务取消等操作
 * 而取消操作，没有任何依据，不知道改取消哪些
 *
 * @author liangb
 * @version 3.0
 * @date 2019/4/29 14:27
 */
@EnableConfigurationProperties(UtsTaskProperties.class)
@Configuration
public class AutoRegistryScanner implements ApplicationListener<ContextRefreshedEvent> {

    @Autowired
    private UtsTaskProperties utsTaskProperties;

    /**
     * 多个数据库实例
     */
    @Autowired(required = false)
    private List<SqlSessionFactory> sqlSessionFactoryList;

    private static Logger logger = LoggerFactory.getLogger(AutoRegistryScanner.class);

    /**
     * 在web 项目中（spring mvc），系统会存在两个容器，一个是root application context,
     * 另一个就是我们自己的 projectName-servlet  context（作为root application context的子容器）。
     * <p>
     * 这种情况下，就会造成onApplicationEvent方法被执行两次。
     * 为了避免上面提到的问题，我们可以只在root application context初始化完成后调用逻辑代码，其他的容器的初始化完成，则不做任何处理.
     * <p>
     * 这里主要做了两件事：
     * 1、添加自定义mybatis的拦截器，完成数据分片的工作
     * 2、注册系统中所有的定时任务
     */
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        BatchInterceptor interceptor = new BatchInterceptor();
        for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
            sqlSessionFactory.getConfiguration().addInterceptor(interceptor);
        }
        if (event.getApplicationContext().getParent() == null) {
            //得到所有的任务
            ClientDetail clientDetail = new ClientDetail();
            //ip由请求自己获取
            //NAME_PATTERN = "${spring.application.name:${vcap.application.name:${spring.config.name:application}}}";
            clientDetail.setSystemName(utsTaskProperties.getSystemName());
            clientDetail.setWeight(utsTaskProperties.getWeight());
            List<TaskJobDetails> taskJobDetailsList = new ArrayList<>();
            clientDetail.setTaskJobDetailsList(taskJobDetailsList);
            for (String basePackage : UtsTaskProperties.basePackage) {
                Set<Class<?>> classesList = getAllByAnnotation(basePackage);
                for (Class classes : classesList) {
                    Method[] methods = classes.getDeclaredMethods();
                    for (Method method : methods) {
                        //得到该类下面的RequestMapping注解
                        UtsTask annotations = method.getDeclaredAnnotation(UtsTask.class);
                        if (null != annotations) {
                            TaskJobDetails scheduleJob = new TaskJobDetails();
                            //设置属性
                            scheduleJob.setJobStatus(annotations.jobStatus());
                            String jobName = annotations.jobName();
                            if (StringUtils.isBlank(jobName)) { //为空时
                                jobName = classes.getSimpleName() + "_" + method.getName();
                            }
                            scheduleJob.setDepend(annotations.depend());
                            scheduleJob.setJobName(jobName);
                            scheduleJob.setCronExpression(annotations.cron());
                            scheduleJob.setJobDesc(annotations.jobDesc());
                            String methodName = annotations.method();
                            if (StringUtils.isBlank(methodName)) {
                                methodName = method.getName();
                            }
                            scheduleJob.setMethod(methodName);
                            String jobGroup = annotations.jobGroup();
                            if (StringUtils.isEmpty(jobGroup)) {
                                jobGroup = classes.getSimpleName();
                            }
                            scheduleJob.setJobGroup(jobGroup);
                            String service = annotations.service();
                            if (StringUtils.isBlank(service)) {
                                service = classes.getSimpleName().substring(0, 1).toLowerCase() + classes.getSimpleName().substring(1);
                            }
                            scheduleJob.setService(service);
                            scheduleJob.setConcurrentNum(annotations.concurrentNum());
                            //任务类型
                            scheduleJob.setTaskType(annotations.taskType());
                            //脚本路径
                            scheduleJob.setScriptPath(annotations.scriptPath());
                            taskJobDetailsList.add(scheduleJob);
                        }
                    }
                }
            }
            //http请求完成注册
            if (!CollectionUtils.isEmpty(taskJobDetailsList)) {
                CloseableHttpClient client = HttpClients.createDefault();
                try {
                    String josnStr = JSONObject.toJSONString(clientDetail);

                    HttpPost httpPost = new HttpPost(utsTaskProperties.getUrl());
                    httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");

                    StringEntity se = new StringEntity(josnStr, "UTF-8");
                    se.setContentType("application/json");
                    httpPost.setEntity(se);

                    CloseableHttpResponse response = client.execute(httpPost);
                    HttpEntity entity = response.getEntity();
                    String result = EntityUtils.toString(entity, "UTF-8");
                    logger.info(String.format("uts 已发起注册，result:%s", result));
                } catch (Exception e) {
                    logger.error("uts 注册异常==>", e);
                } finally {
                    try {
                        client.close();
                    } catch (IOException e) {
                        logger.error("CloseableHttpClient关闭异常==>", e);
                    }
                }
            }
        }
    }

    private Set<Class<?>> getAllByAnnotation(String basePackage) {
        Reflections reflections = new Reflections(basePackage);
        Set<Class<?>> classesList = reflections.getTypesAnnotatedWith(Service.class);
        return classesList;
    }
}
